#!/usr/bin/env node

/**
 * Anthropic API Proxy Server
 * Forwards requests from local port 8082 to https://api.anthropic.com
 * Maintains Claude Code compatible format
 */

import express from 'express';
import axios from 'axios';
import cors from 'cors';
import dotenv from 'dotenv';
import fs from 'fs';
import { createServer } from 'http';
import { Readable } from 'stream';
import path from 'path';
import { fileURLToPath } from 'url';
import { requestStore } from './monitor/store.js';
import { getMonitorHTML } from './monitor/ui.js';
import { getTokenPanelHTML } from './monitor/token-ui.js';
import { getUserManagementHTML } from './monitor/user-ui.js';
import { getStatsDashboardHTML } from './monitor/stats-ui.js';
import { analyzeRequests, generateAnalysisHTML } from './monitor/analysis.js';
import { oauthManager } from './oauth-manager.js';
import { userAuthManager } from './user-auth.js';
import { statsMiddleware, setupStatsAPI } from './stats-integration.js';

// Load environment variables
dotenv.config();

// Get current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// Configuration
const ANTHROPIC_BASE_URL = process.env.ANTHROPIC_BASE_URL || 'https://api.anthropic.com';
const HOST = process.env.HOST || '0.0.0.0';
const PORT = parseInt(process.env.PORT || '8082');
const REQUEST_TIMEOUT = parseInt(process.env.REQUEST_TIMEOUT || '120000'); // in milliseconds
const LOG_LEVEL = process.env.LOG_LEVEL || 'INFO';

// Shared proxy configuration
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY; // Your API key for shared access
const USE_OAUTH = process.env.USE_OAUTH === 'true' || (!ANTHROPIC_API_KEY && oauthManager.isAvailable()); // Auto-detect OAuth
const AUTH_MODE = process.env.AUTH_MODE || 'none'; // none, basic, token
const AUTH_USERNAME = process.env.AUTH_USERNAME || 'admin';
const AUTH_PASSWORD = process.env.AUTH_PASSWORD || 'changeme';
const AUTH_TOKEN = process.env.AUTH_TOKEN;
const RATE_LIMIT_PER_MINUTE = parseInt(process.env.RATE_LIMIT_PER_MINUTE || '60');
const ENABLE_REQUEST_LOG = process.env.ENABLE_REQUEST_LOG === 'true';

// Initialize Express app
const app = express();

// Middleware
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(express.raw({ type: 'application/octet-stream', limit: '50mb' }));

// Define UI routes before static middleware to prevent conflicts
app.get('/tokens', (req, res) => {
  res.send(getTokenPanelHTML());
});

app.get('/users', (req, res) => {
  res.send(getUserManagementHTML());
});

app.get('/stats', (req, res) => {
  res.send(getStatsDashboardHTML());
});

// Static files middleware (after UI routes)
app.use(express.static(path.join(path.dirname(__dirname), 'public')));

// Add statistics tracking middleware
app.use(statsMiddleware);

// Setup statistics API endpoints
setupStatsAPI(app);

// Rate limiting storage
const rateLimitStore = new Map();

// Authentication middleware
function authMiddleware(req, res, next) {
  if (AUTH_MODE === 'none') {
    return next();
  }
  
  if (AUTH_MODE === 'basic') {
    const authHeader = req.headers.authorization;
    if (!authHeader || !authHeader.startsWith('Basic ')) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const credentials = Buffer.from(authHeader.slice(6), 'base64').toString();
    const [username, password] = credentials.split(':');
    
    if (username !== AUTH_USERNAME || password !== AUTH_PASSWORD) {
      return res.status(401).json({ error: 'Invalid credentials' });
    }
  }
  
  if (AUTH_MODE === 'token') {
    const token = req.headers['x-access-token'] || req.headers.authorization?.replace('Bearer ', '');
    if (!token || token !== AUTH_TOKEN) {
      return res.status(401).json({ error: 'Invalid or missing access token' });
    }
  }
  
  next();
}

// Rate limiting middleware
function rateLimitMiddleware(req, res, next) {
  const clientIp = req.ip || req.connection.remoteAddress;
  const now = Date.now();
  const windowStart = now - 60000; // 1 minute window
  
  if (!rateLimitStore.has(clientIp)) {
    rateLimitStore.set(clientIp, []);
  }
  
  const requests = rateLimitStore.get(clientIp);
  const recentRequests = requests.filter(timestamp => timestamp > windowStart);
  
  if (recentRequests.length >= RATE_LIMIT_PER_MINUTE) {
    return res.status(429).json({ 
      error: 'Rate limit exceeded', 
      retryAfter: 60 - Math.floor((now - recentRequests[0]) / 1000) 
    });
  }
  
  recentRequests.push(now);
  rateLimitStore.set(clientIp, recentRequests);
  
  // Clean up old entries periodically
  if (Math.random() < 0.01) {
    for (const [ip, timestamps] of rateLimitStore.entries()) {
      const filtered = timestamps.filter(t => t > windowStart);
      if (filtered.length === 0) {
        rateLimitStore.delete(ip);
      } else {
        rateLimitStore.set(ip, filtered);
      }
    }
  }
  
  next();
}

// Logging utility
class Logger {
  constructor(level = 'INFO') {
    this.levels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
    this.currentLevel = this.levels[level.toUpperCase()] || 1;
  }

  log(level, ...messages) {
    if (this.levels[level] >= this.currentLevel) {
      const timestamp = new Date().toISOString();
      console.log(`${timestamp} - ${level} -`, ...messages);
    }
  }

  debug(...messages) {
    this.log('DEBUG', ...messages);
  }

  info(...messages) {
    this.log('INFO', ...messages);
  }

  warn(...messages) {
    this.log('WARN', ...messages);
  }

  error(...messages) {
    this.log('ERROR', ...messages);
  }
}

const logger = new Logger(LOG_LEVEL);

/**
 * Make API request with automatic token switching on 429 errors
 */
async function makeApiRequest(url, data, headers, options = {}) {
  const maxRetries = oauthManager.getAllTokensStatus().length;
  let lastError = null;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      const response = await axios.post(url, data, {
        ...options,
        headers,
        validateStatus: null // Don't throw on non-2xx status
      });
      
      // If we get a 429 status code (rate limit exceeded), try switching tokens
      if (response.status === 429 && USE_OAUTH && oauthManager.isAvailable() && attempt < maxRetries - 1) {
        console.log(`❌ Rate limit exceeded (429) on attempt ${attempt + 1}`);
        
        // Try to switch to next token
        const switched = await oauthManager.switchToNextToken('API rate limit exceeded (429)');
        if (switched) {
          // Get new headers with the switched token
          headers = await getForwardedHeaders({ headers: {} });
          console.log(`🔄 Retrying request with new token (attempt ${attempt + 2}/${maxRetries})`);
          continue; // Retry with new token
        } else {
          console.log('❌ Could not switch to another token, returning 429 error');
        }
      }
      
      // Return response for any status (including 429 if we can't switch)
      return response;
      
    } catch (error) {
      lastError = error;
      console.error(`❌ Request attempt ${attempt + 1} failed:`, error.message);
      
      // For network errors, don't retry with different tokens
      if (error.code === 'ECONNABORTED' || error.request) {
        break;
      }
    }
  }
  
  // If we get here, all attempts failed
  throw lastError || new Error('All token attempts failed');
}

/**
 * Extract headers to forward from the incoming request
 */
async function getForwardedHeaders(req) {
  const forwardedHeaders = {};
  
  // Add proxy identification header to prevent infinite loops
  forwardedHeaders['x-anthropic-proxy'] = 'true';
  
  logger.info('🔄 ===== HEADER FORWARDING DEBUG =====');
  logger.info(`🔍 USE_OAUTH: ${USE_OAUTH}`);
  logger.info(`🔍 oauthManager.isAvailable(): ${oauthManager ? oauthManager.isAvailable() : 'oauthManager not initialized'}`);
  logger.info(`🔍 ANTHROPIC_API_KEY configured: ${!!ANTHROPIC_API_KEY}`);
  logger.info(`🔍 Request authorization header: ${req.headers['authorization'] ? req.headers['authorization'].substring(0, 20) + '...' : 'not present'}`);
  logger.info(`🔍 Request x-api-key header: ${req.headers['x-api-key'] ? req.headers['x-api-key'].substring(0, 10) + '...' : 'not present'}`);
  
  // Priority 1: Use OAuth if available and enabled
  if (USE_OAUTH && oauthManager.isAvailable()) {
    logger.info('🔐 Using OAuth path for token forwarding');
    // Get access token (will auto-refresh if needed)
    const accessToken = await oauthManager.getAccessToken();
    if (accessToken) {
      forwardedHeaders['authorization'] = `Bearer ${accessToken}`;
      logger.info(`✅ Using OAuth token for request: ${accessToken.substring(0, 10)}...`);
      logger.info(`🔍 OAuth token type: ${accessToken.startsWith('sk-ant-oat') ? 'OAuth Access Token' : 'Unknown OAuth Token Type'}`);
      
      // Log expiry info
      const expiryInfo = oauthManager.getExpiryInfo();
      if (expiryInfo && expiryInfo.needsRefresh) {
        logger.warn(`⚠️ Token expires in ${expiryInfo.expiresInMinutes} minutes`);
      }
    } else {
      logger.warn('⚠️ OAuth is enabled but no valid token available');
    }
  }
  // Priority 2: Use configured API key
  else if (ANTHROPIC_API_KEY) {
    logger.info('🔑 Using server API key path for token forwarding');
    // Use the server's API key for all requests
    forwardedHeaders['x-api-key'] = ANTHROPIC_API_KEY;
    logger.info(`✅ Using server API key for request: ${ANTHROPIC_API_KEY.substring(0, 10)}...`);
  }
  // Priority 3: Use client's credentials (only if not authenticated via user system)
  else {
    logger.info('🔄 Using client credentials path for token forwarding');
    
    // If the request was authenticated via user system, don't forward user token
    if (req.authenticatedUser) {
      logger.error('❌ User authenticated but no OAuth/API key configured to forward to Anthropic');
      logger.error('   Please configure either USE_OAUTH with .authtoken or ANTHROPIC_API_KEY');
      // Don't forward user token to Anthropic
      forwardedHeaders['x-api-key'] = ''; // Empty key will cause Anthropic to reject
    } else {
      // Fall back to client's API key if no server key is configured and no user auth
      const clientApiKey = req.headers['x-api-key'] || req.headers['authorization'];
      if (clientApiKey) {
        if (req.headers['x-api-key']) {
          forwardedHeaders['x-api-key'] = clientApiKey;
          logger.info(`✅ Using client x-api-key: ${clientApiKey.substring(0, 10)}...`);
        } else {
          forwardedHeaders['authorization'] = clientApiKey;
          logger.info(`✅ Using client authorization header: ${clientApiKey.substring(0, 20)}...`);
        }
      } else {
        logger.warn('⚠️ No authentication credentials available');
      }
    }
  }
  
  // Forward other headers
  const headersToForward = [
    'anthropic-version',
    'anthropic-beta',
    'user-agent'
  ];
  
  headersToForward.forEach(headerName => {
    const headerValue = req.headers[headerName];
    if (headerValue) {
      forwardedHeaders[headerName] = headerValue;
    }
  });
  
  // Ensure anthropic-version is set
  if (!forwardedHeaders['anthropic-version']) {
    forwardedHeaders['anthropic-version'] = '2023-06-01';
  }
  
  // Ensure content-type is set
  forwardedHeaders['content-type'] = 'application/json';
  
  logger.info('🔄 Final forwarded headers:');
  Object.entries(forwardedHeaders).forEach(([key, value]) => {
    if (key.toLowerCase() === 'authorization' || key.toLowerCase() === 'x-api-key') {
      logger.info(`   ${key}: ${value.substring(0, 20)}...`);
    } else {
      logger.info(`   ${key}: ${value}`);
    }
  });
  logger.info('🔄 ===== END HEADER FORWARDING DEBUG =====');
  
  return forwardedHeaders;
}

/**
 * Main messages endpoint - proxies to Anthropic API
 */
app.post('/v1/messages', userAuthManager.middleware(), authMiddleware, rateLimitMiddleware, async (req, res) => {
  // Check for infinite loop - if request has our proxy header, reject it
  if (req.headers['x-anthropic-proxy']) {
    logger.error('❌ INFINITE LOOP DETECTED: Request contains proxy header');
    return res.status(400).json({ 
      error: 'Infinite loop detected. The proxy is trying to forward requests to itself.',
      solution: 'Please check your ANTHROPIC_BASE_URL configuration.'
    });
  }
  
  // Start monitoring this request
  const monitorId = requestStore.startRequest(req);
  req.monitorId = monitorId;
  try {
    // Log incoming request details
    logger.info('='.repeat(60));
    logger.info('📥 INCOMING REQUEST');
    logger.info(`Client IP: ${req.ip || req.connection.remoteAddress}`);
    logger.info(`Model: ${req.body.model}`);
    logger.info(`Stream: ${req.body.stream}`);
    logger.info(`Max Tokens: ${req.body.max_tokens}`);
    logger.info(`Temperature: ${req.body.temperature}`);
    
    // Debug: Show if user was authenticated and which token was used
    if (req.authenticatedUser) {
      logger.info(`🔐 Authenticated User: ${req.authenticatedUser.name}`);
      logger.info(`🔐 User Token Type: Custom User Token`);
    } else {
      logger.info('🔓 No user authentication performed (authentication disabled)');
    }
    
    // Log authentication method
    if (USE_OAUTH && oauthManager.isAvailable()) {
      logger.info('🔐 Using OAuth authentication');
    } else if (ANTHROPIC_API_KEY) {
      logger.info('🔑 Using shared proxy mode with server API key');
    }
    
    // Log headers
    logger.info('Headers from Claude Code:');
    Object.entries(req.headers).forEach(([headerName, headerValue]) => {
      if (['x-api-key', 'authorization'].includes(headerName.toLowerCase())) {
        // Mask sensitive data
        const maskedValue = headerValue.length > 10 
          ? headerValue.substring(0, 10) + '...' 
          : headerValue;
        logger.info(`  ${headerName}: ${maskedValue}`);
      } else {
        logger.info(`  ${headerName}: ${headerValue}`);
      }
    });
    
    // Forward all headers from Claude Code
    const headers = await getForwardedHeaders(req);
    
    // Log COMPLETE request body (DEBUG only)
    logger.debug('Request Body (COMPLETE):');
    logger.debug(JSON.stringify(req.body, null, 2));
    logger.info('='.repeat(60));
    
    if (req.body.stream) {
      // Handle streaming response with automatic token switching
      await makeStreamingApiRequest(
        `${ANTHROPIC_BASE_URL}/v1/messages`,
        req.body,
        headers,
        req,
        res,
        {
          timeout: REQUEST_TIMEOUT
        }
      );
    } else {
      // Handle non-streaming response with automatic token switching
      const response = await makeApiRequest(
        `${ANTHROPIC_BASE_URL}/v1/messages`,
        req.body,
        headers,
        {
          timeout: REQUEST_TIMEOUT
        }
      );
      
      if (response.status !== 200) {
        logger.error('='.repeat(60));
        logger.error('❌ ERROR RESPONSE FROM ANTHROPIC');
        logger.error(`Status Code: ${response.status}`);
        logger.debug(`Response Headers: ${JSON.stringify(response.headers)}`);
        logger.debug(`Response Body: ${JSON.stringify(response.data)}`);
        logger.error('='.repeat(60));
        
        res.status(response.status).json(response.data);
        return;
      }
      
      // Log successful response
      logger.info('='.repeat(60));
      logger.info('✅ SUCCESSFUL RESPONSE FROM ANTHROPIC');
      logger.info(`Status Code: ${response.status}`);
      logger.debug(`Response Headers: ${JSON.stringify(response.headers)}`);
      logger.debug('Response Body (COMPLETE):');
      logger.debug(JSON.stringify(response.data, null, 2));
      logger.info('='.repeat(60));
      
      // Update monitor with response
      requestStore.endRequest(req.monitorId, {
        status: response.status,
        headers: response.headers,
        body: response.data
      });
      
      res.json(response.data);
    }
  } catch (error) {
    // Log error to monitor
    if (req.monitorId) {
      requestStore.setError(req.monitorId, error);
    }
    
    if (error.code === 'ECONNABORTED') {
      logger.error('Request to Anthropic API timed out');
      res.status(504).json({ error: 'Request timeout' });
    } else if (error.request) {
      logger.error(`Network error: ${error.message}`);
      res.status(502).json({ error: `Network error: ${error.message}` });
    } else {
      logger.error(`Unexpected error: ${error.message}`, error.stack);
      res.status(500).json({ error: `Internal server error: ${error.message}` });
    }
  }
});

/**
 * Make streaming API request with automatic token switching on 429 errors
 */
async function makeStreamingApiRequest(url, data, headers, req, res, options = {}) {
  const maxRetries = oauthManager.getAllTokensStatus().length;
  
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      logger.info('='.repeat(60));
      logger.info(`🔄 STARTING STREAMING RESPONSE (attempt ${attempt + 1})`);
      logger.info(`Forwarding to: ${url}`);
      logger.info(`Headers being sent: ${JSON.stringify(headers)}`);
      logger.info('='.repeat(60));

      const response = await axios.post(url, data, {
        ...options,
        headers,
        responseType: 'stream',
        validateStatus: null
      });

      // If we get a 429 status code (rate limit exceeded), try switching tokens
      if (response.status === 429 && USE_OAUTH && oauthManager.isAvailable() && attempt < maxRetries - 1) {
        console.log(`❌ Rate limit exceeded (429) on streaming attempt ${attempt + 1}`);
        
        // Try to switch to next token
        const switched = await oauthManager.switchToNextToken('API rate limit exceeded (429)');
        if (switched) {
          // Get new headers with the switched token
          headers = await getForwardedHeaders({ headers: {} });
          console.log(`🔄 Retrying streaming request with new token (attempt ${attempt + 2}/${maxRetries})`);
          response.data.destroy(); // Clean up the failed stream
          continue; // Retry with new token
        } else {
          console.log('❌ Could not switch to another token for streaming request');
        }
      }

      // Handle successful response or final failure
      return await processStreamingResponse(response, req, res);

    } catch (error) {
      logger.error(`❌ Streaming attempt ${attempt + 1} failed:`, error.message);
      
      // For network errors, don't retry with different tokens
      if (error.code === 'ECONNABORTED' || error.request) {
        throw error;
      }
    }
  }
  
  throw new Error('All streaming token attempts failed');
}

/**
 * Process the streaming response from Anthropic API
 */
async function processStreamingResponse(response, req, res) {
  let responseEnded = false;
  let streamDestroyed = false;
  
  try {
    const collectedResponse = []; // Collect response chunks for logging
    
    // Set SSE headers if not already set
    if (!res.headersSent) {
      res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Headers': '*'
      });
    }
    
    if (response.status !== 200) {
      const errorChunks = [];
      response.data.on('data', chunk => errorChunks.push(chunk));
      response.data.on('end', () => {
        if (responseEnded) return;
        const errorText = Buffer.concat(errorChunks).toString();
        logger.error('='.repeat(60));
        logger.error('❌ STREAMING ERROR FROM ANTHROPIC');
        logger.error(`Status Code: ${response.status}`);
        logger.error(`Error Body: ${errorText}`);
        logger.error('='.repeat(60));
        
        if (!res.headersSent) {
          res.writeHead(response.status, { 'Content-Type': 'text/event-stream' });
        }
        res.write(`data: ${JSON.stringify({
          type: 'error',
          error: { type: 'api_error', message: errorText }
        })}\n\n`);
        res.end();
        responseEnded = true;
      });
      return;
    }
    
    let buffer = '';
    
    response.data.on('data', chunk => {
      if (responseEnded || streamDestroyed) return;
      
      buffer += chunk.toString();
      const lines = buffer.split('\n');
      buffer = lines.pop(); // Keep incomplete line in buffer
      
      lines.forEach(line => {
        if (responseEnded || streamDestroyed) return;
        
        if (line.trim()) {
          // Log COMPLETE chunk content (DEBUG only)
          if (line.startsWith('data: ')) {
            const chunkData = line.substring(6);
            if (chunkData.trim() && chunkData.trim() !== '[DONE]') {
              logger.debug(`📤 Stream chunk: ${chunkData}`);
              collectedResponse.push(chunkData);
              
              // Add to monitor
              if (req.monitorId) {
                requestStore.addStreamChunk(req.monitorId, chunkData);
              }
            }
          }
          
          try {
            res.write(`${line}\n`);
            if (line.startsWith('data: ')) {
              res.write('\n'); // Add extra newline after data lines for SSE format
            }
          } catch (writeError) {
            logger.error(`Error writing to response: ${writeError.message}`);
            streamDestroyed = true;
          }
        }
      });
    });
    
    response.data.on('end', () => {
      if (responseEnded) return;
      
      // Process any remaining data in buffer
      if (buffer.trim() && !streamDestroyed) {
        try {
          res.write(`${buffer}\n\n`);
        } catch (writeError) {
          logger.error(`Error writing final buffer: ${writeError.message}`);
        }
      }
      
      logger.info('='.repeat(60));
      logger.info('✅ STREAMING COMPLETED');
      logger.info(`Total chunks received: ${collectedResponse.length}`);
      
      // Log COMPLETE aggregated response
      if (collectedResponse.length > 0) {
        logger.info('='.repeat(60));
        logger.info('📝 STREAMING RESPONSE ANALYSIS:');
        
        // Reconstruct full response content from all chunks
        const fullTextParts = [];
        const toolCalls = [];
        let messageComplete = false;
        
        collectedResponse.forEach(chunkStr => {
          try {
            const chunk = JSON.parse(chunkStr);
            
            // Handle different chunk types
            if (chunk.type === 'content_block_delta') {
              const delta = chunk.delta || {};
              if (delta.type === 'text_delta' && delta.text) {
                fullTextParts.push(delta.text);
              } else if (delta.type === 'input_json_delta' && delta.partial_json) {
                // Tool use input
                fullTextParts.push(delta.partial_json);
              }
            } else if (chunk.type === 'content_block_start') {
              const contentBlock = chunk.content_block || {};
              if (contentBlock.type === 'tool_use') {
                toolCalls.push({
                  id: contentBlock.id,
                  name: contentBlock.name,
                  input: ''
                });
              }
            } else if (chunk.type === 'message_stop') {
              messageComplete = true;
            }
          } catch (e) {
            // Ignore parse errors
          }
        });
        
        // Output merged complete content as JSON (DEBUG only)
        logger.info('='.repeat(60));
        logger.info('🔗 MERGED COMPLETE CONTENT (JSON):');
        
        const mergedContent = {
          completeText: fullTextParts.join(''),
          totalCharacters: fullTextParts.join('').length,
          toolCalls: toolCalls,
          messageComplete: messageComplete,
          timestamp: new Date().toISOString()
        };
        
        logger.debug(JSON.stringify(mergedContent, null, 2));
        
        // Save merged content to monitor
        if (req.monitorId) {
          requestStore.setMergedContent(req.monitorId, mergedContent);
        }
        
        // Also log individual chunks for debugging (DEBUG only)
        logger.debug('='.repeat(60));
        logger.debug('📦 INDIVIDUAL CHUNKS (for debugging):');
        collectedResponse.forEach((chunk, i) => {
          logger.debug(`Chunk ${i + 1}: ${chunk}`);
        });
      }
      
      logger.info('='.repeat(60));
      
      if (!responseEnded && !streamDestroyed) {
        res.end();
        responseEnded = true;
        
        // Mark request as completed in monitor
        if (req.monitorId) {
          requestStore.endRequest(req.monitorId, {
            status: 200,
            headers: response.headers,
            body: { streaming: true, chunks: collectedResponse.length }
          });
        }
      }
    });
    
    response.data.on('error', error => {
      if (responseEnded) return;
      
      logger.error(`Stream error: ${error.message}`);
      if (!streamDestroyed) {
        try {
          res.write(`data: ${JSON.stringify({
            type: 'error',
            error: { type: 'stream_error', message: error.message }
          })}\n\n`);
        } catch (writeError) {
          logger.error(`Error writing error message: ${writeError.message}`);
        }
      }
      
      if (!responseEnded) {
        res.end();
        responseEnded = true;
      }
    });
    
    // Handle client disconnect
    req.on('close', () => {
      logger.info('Client disconnected, stopping stream');
      responseEnded = true;
      streamDestroyed = true;
      if (response && response.data) {
        response.data.destroy();
      }
    });
    
    // Handle response errors
    res.on('error', (error) => {
      logger.error(`Response error: ${error.message}`);
      responseEnded = true;
      streamDestroyed = true;
    });
    
  } catch (error) {
    logger.error(`Streaming error: ${error.message}`, error.stack);
    
    if (!responseEnded && !streamDestroyed) {
      if (!res.headersSent) {
        res.writeHead(500, { 'Content-Type': 'text/event-stream' });
      }
      
      try {
        res.write(`data: ${JSON.stringify({
          type: 'error',
          error: { type: 'stream_error', message: error.message }
        })}\n\n`);
      } catch (writeError) {
        logger.error(`Error writing error response: ${writeError.message}`);
      }
      
      res.end();
      responseEnded = true;
    }
  }
}

/**
 * Token counting endpoint
 */
app.post('/v1/messages/count_tokens', userAuthManager.middleware(), authMiddleware, rateLimitMiddleware, async (req, res) => {
  // Check for infinite loop - if request has our proxy header, reject it
  if (req.headers['x-anthropic-proxy']) {
    logger.error('❌ INFINITE LOOP DETECTED: Token count request contains proxy header');
    return res.status(400).json({ 
      error: 'Infinite loop detected. The proxy is trying to forward requests to itself.',
      solution: 'Please check your ANTHROPIC_BASE_URL configuration.'
    });
  }
  
  try {
    logger.info('='.repeat(60));
    logger.info('🔢 TOKEN COUNT REQUEST');
    logger.debug('Request (COMPLETE):');
    logger.debug(JSON.stringify(req.body, null, 2));
    logger.info('='.repeat(60));
    
    // Forward all headers from Claude Code
    const headers = await getForwardedHeaders(req);
    
    const response = await axios.post(
      `${ANTHROPIC_BASE_URL}/v1/messages/count_tokens`,
      req.body,
      {
        headers,
        timeout: 30000,
        validateStatus: null
      }
    );
    
    if (response.status !== 200) {
      logger.error(`Token count error: ${response.status} - ${JSON.stringify(response.data)}`);
      res.status(response.status).json(response.data);
      return;
    }
    
    logger.info('='.repeat(60));
    logger.info('✅ TOKEN COUNT RESPONSE');
    logger.debug('Result (COMPLETE):');
    logger.debug(JSON.stringify(response.data, null, 2));
    logger.info('='.repeat(60));
    
    res.json(response.data);
  } catch (error) {
    logger.error(`Token counting error: ${error.message}`, error.stack);
    res.status(500).json({ error: error.message });
  }
});

/**
 * OAuth endpoints - proxies to Anthropic OAuth API
 * Handles all /v1/oauth/* requests
 */
app.all('/v1/oauth/*', userAuthManager.middleware(), authMiddleware, async (req, res) => {
  // Check for infinite loop - if request has our proxy header, reject it
  if (req.headers['x-anthropic-proxy']) {
    logger.error('❌ INFINITE LOOP DETECTED: OAuth request contains proxy header');
    return res.status(400).json({ 
      error: 'Infinite loop detected. The proxy is trying to forward requests to itself.',
      solution: 'Please check your ANTHROPIC_BASE_URL configuration.'
    });
  }
  
  const monitorId = requestStore.startRequest(req);
  req.monitorId = monitorId;
  
  try {
    const oauthPath = req.path; // e.g., /v1/oauth/refresh, /v1/oauth/authorize
    const method = req.method.toLowerCase();
    
    logger.info('='.repeat(60));
    logger.info('🔐 OAUTH REQUEST');
    logger.info(`Method: ${req.method}`);
    logger.info(`Path: ${oauthPath}`);
    logger.info(`Client IP: ${req.ip || req.connection.remoteAddress}`);
    
    if (req.body && Object.keys(req.body).length > 0) {
      logger.debug('Request Body:');
      logger.debug(JSON.stringify(req.body, null, 2));
    }
    logger.info('='.repeat(60));
    
    // Forward all headers
    const headers = await getForwardedHeaders(req);
    
    // Build the full URL
    const targetUrl = `${ANTHROPIC_BASE_URL}${oauthPath}`;
    logger.info(`Forwarding to: ${targetUrl}`);
    
    // Make the request based on method type
    let response;
    const axiosConfig = {
      headers,
      timeout: 30000,
      validateStatus: null // Don't throw on non-2xx status
    };
    
    switch (method) {
      case 'get':
        response = await axios.get(targetUrl, axiosConfig);
        break;
      case 'post':
        response = await axios.post(targetUrl, req.body || {}, axiosConfig);
        break;
      case 'put':
        response = await axios.put(targetUrl, req.body || {}, axiosConfig);
        break;
      case 'patch':
        response = await axios.patch(targetUrl, req.body || {}, axiosConfig);
        break;
      case 'delete':
        response = await axios.delete(targetUrl, axiosConfig);
        break;
      default:
        logger.warn(`Unsupported OAuth method: ${method}`);
        res.status(405).json({ error: `Method ${method} not allowed` });
        requestStore.updateRequest(monitorId, {
          response: { status: 405 },
          error: `Method ${method} not allowed`
        });
        return;
    }
    
    // Log response
    logger.info('='.repeat(60));
    if (response.status >= 200 && response.status < 300) {
      logger.info('✅ OAUTH RESPONSE SUCCESS');
    } else {
      logger.warn(`⚠️ OAUTH RESPONSE STATUS: ${response.status}`);
    }
    logger.info(`Status: ${response.status}`);
    
    // Mask sensitive data in logs
    const responseData = response.data;
    if (responseData && typeof responseData === 'object') {
      const maskedData = { ...responseData };
      if (maskedData.access_token) {
        maskedData.access_token = maskedData.access_token.substring(0, 10) + '...';
      }
      if (maskedData.refresh_token) {
        maskedData.refresh_token = maskedData.refresh_token.substring(0, 10) + '...';
      }
      logger.debug('Response (masked):');
      logger.debug(JSON.stringify(maskedData, null, 2));
    }
    logger.info('='.repeat(60));
    
    // Update monitoring data
    requestStore.updateRequest(monitorId, {
      response: {
        status: response.status,
        headers: response.headers,
        data: response.data
      }
    });
    
    // Forward response headers
    Object.keys(response.headers).forEach(key => {
      if (!['content-encoding', 'transfer-encoding'].includes(key.toLowerCase())) {
        res.set(key, response.headers[key]);
      }
    });
    
    // Send response
    res.status(response.status).json(response.data);
    
  } catch (error) {
    logger.error(`OAuth proxy error: ${error.message}`, error.stack);
    
    requestStore.updateRequest(monitorId, {
      response: { status: 500 },
      error: error.message
    });
    
    res.status(500).json({ 
      error: 'OAuth proxy error',
      message: error.message 
    });
  }
});

/**
 * Health check endpoint
 */
// User Management API endpoints
app.get('/api/users', (req, res) => {
  try {
    const users = userAuthManager.getAllUsersAdmin();
    res.json(users);
  } catch (error) {
    logger.error(`Get users error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/users', (req, res) => {
  try {
    const { name, token } = req.body;
    const newUser = userAuthManager.addUser(name, token);
    res.json(newUser);
  } catch (error) {
    logger.error(`Add user error: ${error.message}`);
    res.status(400).json({ error: error.message });
  }
});

app.put('/api/users/:index', (req, res) => {
  try {
    const index = parseInt(req.params.index);
    const { name, token } = req.body;
    const updatedUser = userAuthManager.updateUser(index, name, token);
    res.json(updatedUser);
  } catch (error) {
    logger.error(`Update user error: ${error.message}`);
    res.status(400).json({ error: error.message });
  }
});

app.delete('/api/users/:index', (req, res) => {
  try {
    const index = parseInt(req.params.index);
    userAuthManager.deleteUser(index);
    res.json({ success: true });
  } catch (error) {
    logger.error(`Delete user error: ${error.message}`);
    res.status(400).json({ error: error.message });
  }
});

// User management UI moved to before static middleware

app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    anthropic_base_url: ANTHROPIC_BASE_URL,
    proxy_version: '1.0.0'
  });
});

/**
 * Root endpoint with API information
 */
app.get('/', (req, res) => {
  res.json({
    message: 'Anthropic API Proxy',
    status: 'running',
    version: '1.0.0',
    mode: USE_OAUTH ? 'oauth' : (ANTHROPIC_API_KEY ? 'shared-proxy' : 'pass-through'),
    endpoints: {
      messages: '/v1/messages',
      count_tokens: '/v1/messages/count_tokens',
      health: '/health',
      monitor: '/monitor',
      tokens: '/tokens',
      status: '/status'
    },
    configuration: {
      target_api: ANTHROPIC_BASE_URL,
      timeout: REQUEST_TIMEOUT,
      auth_required: AUTH_MODE !== 'none',
      auth_mode: AUTH_MODE,
      rate_limit: RATE_LIMIT_PER_MINUTE + ' requests/minute'
    },
    usage_instructions: ANTHROPIC_API_KEY ? 
      'This proxy uses OAuth authentication from .authtoken file.' :
      (ANTHROPIC_API_KEY ? 
        'This proxy uses a shared API key. No need to provide your own API key.' :
        'Please provide your own Anthropic API key via x-api-key header.')
  });
});


// Monitor API endpoints
app.get('/monitor', (req, res) => {
  res.send(getMonitorHTML());
});

// Token and stats endpoints moved to before static middleware

app.get('/api/monitor/requests', (req, res) => {
  try {
    const filters = {
      status: req.query.status,
      model: req.query.model,
      timeRange: req.query.timeRange,
      page: parseInt(req.query.page) || 1,
      limit: parseInt(req.query.limit) || 50
    };
    
    const result = requestStore.getAll(filters);
    res.json(result);
  } catch (error) {
    logger.error(`Monitor API error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/monitor/requests/:id', (req, res) => {
  try {
    const request = requestStore.getById(req.params.id);
    if (!request) {
      return res.status(404).json({ error: 'Request not found' });
    }
    res.json(request);
  } catch (error) {
    logger.error(`Monitor API error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/monitor/config', (req, res) => {
  res.json({
    logLevel: LOG_LEVEL,
    port: PORT,
    host: HOST,
    mode: ANTHROPIC_API_KEY ? 'shared-proxy' : 'pass-through',
    authMode: AUTH_MODE,
    rateLimit: RATE_LIMIT_PER_MINUTE
  });
});

/**
 * Force OAuth token refresh endpoint
 */
app.get('/api/oauth/refresh', async (req, res) => {
  if (!USE_OAUTH || !oauthManager.isAvailable()) {
    return res.status(400).json({ 
      error: 'OAuth is not configured',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    logger.info('Manual OAuth token refresh requested');
    const success = await oauthManager.forceRefresh();
    
    if (success) {
      const expiryInfo = oauthManager.getExpiryInfo();
      res.json({
        success: true,
        message: 'Token refreshed successfully',
        expiryInfo
      });
    } else {
      res.status(500).json({
        success: false,
        error: 'Failed to refresh token'
      });
    }
  } catch (error) {
    logger.error('Error refreshing token:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * OAuth status endpoint
 */
app.get('/api/oauth/status', (req, res) => {
  if (!USE_OAUTH || !oauthManager.isAvailable()) {
    return res.json({ 
      configured: false,
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  const expiryInfo = oauthManager.getExpiryInfo();
  const allTokensStatus = oauthManager.getAllTokensStatus();
  
  res.json({
    configured: true,
    subscription: oauthManager.getSubscriptionType(),
    expiry: expiryInfo,
    currentTokenIndex: oauthManager.currentTokenIndex,
    totalTokens: allTokensStatus.length,
    allTokens: allTokensStatus
  });
});

/**
 * Switch to specific token endpoint
 */
app.post('/api/oauth/switch/:tokenIndex', async (req, res) => {
  if (!USE_OAUTH || !oauthManager.isAvailable()) {
    return res.status(400).json({ 
      error: 'OAuth is not configured',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  const tokenIndex = parseInt(req.params.tokenIndex);
  
  if (isNaN(tokenIndex) || tokenIndex < 0 || tokenIndex >= oauthManager.tokens.length) {
    return res.status(400).json({
      error: 'Invalid token index',
      validRange: `0-${oauthManager.tokens.length - 1}`
    });
  }

  try {
    const oldIndex = oauthManager.currentTokenIndex;
    oauthManager.currentTokenIndex = tokenIndex;
    
    logger.info(`Manual token switch from ${oldIndex + 1} to ${tokenIndex + 1}`);
    
    // Check if the new token is valid and refresh if needed
    const newToken = oauthManager.getCurrentToken();
    const expiresIn = newToken.expiresAt - Date.now();
    
    if (expiresIn <= 5 * 60 * 1000) { // Within 5 minutes
      logger.info(`Token ${tokenIndex + 1} needs refresh, refreshing...`);
      await oauthManager.refreshToken(tokenIndex);
    }

    const expiryInfo = oauthManager.getExpiryInfo();
    res.json({
      success: true,
      message: `Switched to token ${tokenIndex + 1}`,
      previousTokenIndex: oldIndex,
      currentTokenIndex: tokenIndex,
      expiryInfo
    });
  } catch (error) {
    logger.error('Error switching token:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Force refresh all tokens endpoint
 */
app.post('/api/oauth/refresh/all', async (req, res) => {
  if (!USE_OAUTH || !oauthManager.isAvailable()) {
    return res.status(400).json({ 
      error: 'OAuth is not configured',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    logger.info('Manual refresh for all tokens requested');
    const success = await oauthManager.forceRefreshAll();
    
    const allTokensStatus = oauthManager.getAllTokensStatus();
    
    res.json({
      success: success,
      message: success ? 'All tokens refreshed successfully' : 'Some token refreshes failed',
      allTokens: allTokensStatus
    });
  } catch (error) {
    logger.error('Error refreshing all tokens:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Reload tokens from file endpoint
 */
app.post('/api/oauth/reload', async (req, res) => {
  if (!USE_OAUTH) {
    return res.status(400).json({ 
      error: 'OAuth is not enabled',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    logger.info('Manual token reload requested');
    const oldTokenCount = oauthManager.tokens.length;
    
    // Reload tokens from file
    oauthManager.reload();
    
    const newTokenCount = oauthManager.tokens.length;
    const allTokensStatus = oauthManager.getAllTokensStatus();
    
    logger.info(`Token reload completed: ${oldTokenCount} → ${newTokenCount} tokens`);
    
    res.json({
      success: true,
      message: `Tokens reloaded successfully`,
      previousTokenCount: oldTokenCount,
      currentTokenCount: newTokenCount,
      currentTokenIndex: oauthManager.currentTokenIndex,
      allTokens: allTokensStatus
    });
  } catch (error) {
    logger.error('Error reloading tokens:', error);
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Add a new OAuth token
 */
app.post('/api/oauth/tokens', async (req, res) => {
  if (!USE_OAUTH) {
    return res.status(400).json({ 
      error: 'OAuth is not enabled',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    const { token, name, force } = req.body;
    
    if (!token) {
      return res.status(400).json({ error: 'Token data is required' });
    }

    // If name is provided, add it to the token data
    let tokenData = typeof token === 'string' ? JSON.parse(token) : token;
    if (name) {
      tokenData.name = name;
    }

    const result = oauthManager.addToken(tokenData, { force });
    
    // If duplicate found and not forcing, return duplicate info
    if (!result.success && result.duplicate) {
      return res.status(409).json(result);
    }
    
    if (!result.success) {
      return res.status(400).json(result);
    }
    
    const allTokensStatus = oauthManager.getAllTokensStatus();
    
    logger.info(`Token ${result.overwritten ? 'overwritten' : 'added'}: ${result.name} at index ${result.index}`);
    
    res.json({
      ...result,
      allTokens: allTokensStatus
    });
  } catch (error) {
    logger.error('Error adding token:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Add multiple OAuth tokens (bulk)
 */
app.post('/api/oauth/tokens/bulk', async (req, res) => {
  if (!USE_OAUTH) {
    return res.status(400).json({ 
      error: 'OAuth is not enabled',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    const { tokens } = req.body;
    
    if (!tokens) {
      return res.status(400).json({ error: 'Tokens array is required' });
    }

    const result = oauthManager.addTokensBulk(tokens);
    const allTokensStatus = oauthManager.getAllTokensStatus();
    
    logger.info(`${result.added} tokens added in bulk`);
    
    res.json({
      ...result,
      allTokens: allTokensStatus
    });
  } catch (error) {
    logger.error('Error adding tokens in bulk:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * Delete an OAuth token
 */
app.delete('/api/oauth/tokens/:index', async (req, res) => {
  if (!USE_OAUTH) {
    return res.status(400).json({ 
      error: 'OAuth is not enabled',
      mode: ANTHROPIC_API_KEY ? 'api-key' : 'pass-through'
    });
  }

  try {
    const tokenIndex = parseInt(req.params.index);
    
    if (isNaN(tokenIndex)) {
      return res.status(400).json({ error: 'Invalid token index' });
    }

    const result = oauthManager.deleteToken(tokenIndex);
    const allTokensStatus = oauthManager.getAllTokensStatus();
    
    logger.info(`Token deleted: ${result.deletedName} at index ${result.deletedIndex}`);
    
    res.json({
      ...result,
      allTokens: allTokensStatus
    });
  } catch (error) {
    logger.error('Error deleting token:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

/**
 * User authentication status endpoint
 */
app.get('/api/users/status', (req, res) => {
  res.json(userAuthManager.getStatus());
});

/**
 * Reload users from .userlist file
 */
app.post('/api/users/reload', (req, res) => {
  userAuthManager.reload();
  res.json({
    success: true,
    ...userAuthManager.getStatus()
  });
});

/**
 * Status endpoint for shared proxy mode
 */
app.get('/status', (req, res) => {
  const totalRequests = requestStore.getAll({ limit: 1 }).total || 0;
  const rateLimitInfo = {};
  
  // Calculate current rate limit usage
  const now = Date.now();
  const windowStart = now - 60000;
  for (const [ip, timestamps] of rateLimitStore.entries()) {
    const recentRequests = timestamps.filter(t => t > windowStart);
    if (recentRequests.length > 0) {
      rateLimitInfo[ip] = {
        requests: recentRequests.length,
        limit: RATE_LIMIT_PER_MINUTE,
        remaining: Math.max(0, RATE_LIMIT_PER_MINUTE - recentRequests.length)
      };
    }
  }
  
  const oauthExpiry = USE_OAUTH && oauthManager.isAvailable() ? oauthManager.getExpiryInfo() : null;
  const allTokensStatus = USE_OAUTH && oauthManager.isAvailable() ? oauthManager.getAllTokensStatus() : null;
  
  res.json({
    proxy_mode: USE_OAUTH ? 'oauth' : (ANTHROPIC_API_KEY ? 'shared' : 'pass-through'),
    api_key_configured: !!ANTHROPIC_API_KEY,
    oauth_configured: USE_OAUTH && oauthManager.isAvailable(),
    oauth_subscription: USE_OAUTH ? oauthManager.getSubscriptionType() : null,
    oauth_expiry: oauthExpiry,
    oauth_auto_refresh: USE_OAUTH && oauthManager.isAvailable(),
    oauth_tokens: {
      total: allTokensStatus ? allTokensStatus.length : 0,
      current_index: USE_OAUTH && oauthManager.isAvailable() ? oauthManager.currentTokenIndex : null,
      status: allTokensStatus
    },
    user_authentication: userAuthManager.getStatus(),
    authentication: {
      enabled: AUTH_MODE !== 'none',
      mode: AUTH_MODE
    },
    rate_limiting: {
      enabled: true,
      limit_per_minute: RATE_LIMIT_PER_MINUTE,
      active_clients: Object.keys(rateLimitInfo).length,
      current_usage: rateLimitInfo
    },
    statistics: {
      total_requests: totalRequests,
      uptime: process.uptime(),
      memory_usage: process.memoryUsage()
    },
    instructions: {
      claude_code_setup: 'claude --base-url http://' + HOST + ':' + PORT + ' OR export ANTHROPIC_API_BASE_URL=http://' + HOST + ':' + PORT,
      authentication: AUTH_MODE === 'basic' ? 
        'Use Basic Auth with username/password' : 
        AUTH_MODE === 'token' ? 
        'Include x-access-token header with your token' : 
        'No authentication required',
      api_key: USE_OAUTH ? 
        'Using OAuth authentication from .authtoken file' :
        (ANTHROPIC_API_KEY ? 
          'API key is configured. No need to provide your own.' : 
          'Provide your Anthropic API key via x-api-key header')
    }
  });
});

app.get('/api/monitor/stats', (req, res) => {
  try {
    // Read stats from user-stats.json file
    const statsPath = path.join(path.dirname(__dirname), 'data', 'user-stats.json');
    if (fs.existsSync(statsPath)) {
      const statsData = fs.readFileSync(statsPath, 'utf8');
      const stats = JSON.parse(statsData);
      return res.json(stats);
    }
    
    // Fallback to in-memory stats if file doesn't exist
    // Get filters from query parameters
    const filters = {
      status: req.query.status,
      model: req.query.model,
      timeRange: req.query.timeRange
    };
    
    // Remove empty filters
    Object.keys(filters).forEach(key => {
      if (!filters[key]) delete filters[key];
    });
    
    let stats;
    if (Object.keys(filters).length > 0) {
      // Calculate stats for filtered data
      const filteredRequests = requestStore.getAll(filters);
      const requests = filteredRequests.data || [];
      
      let successCount = 0;
      let errorCount = 0;
      let totalDuration = 0;
      let totalInputTokens = 0;
      let totalOutputTokens = 0;
      
      requests.forEach(request => {
        const responseStatus = request.response?.status;
        if (responseStatus >= 200 && responseStatus < 300 || request.status === 'success') {
          successCount++;
        } else if (responseStatus >= 400 || request.status === 'error' || request.error) {
          errorCount++;
        }
        
        totalDuration += request.metrics?.duration || 0;
        totalInputTokens += request.metrics?.inputTokens || 0;
        totalOutputTokens += request.metrics?.outputTokens || 0;
      });
      
      const avgDuration = requests.length > 0 ? Math.round(totalDuration / requests.length) : 0;
      const successRate = requests.length > 0 ? ((successCount / requests.length) * 100).toFixed(2) : 0;
      const activeRequests = requests.filter(r => r.status === 'pending').length;
      
      stats = {
        totalRequests: requests.length,
        successCount: successCount,
        errorCount: errorCount,
        successRate: successRate + '%',
        avgDuration: avgDuration + 'ms',
        totalInputTokens: totalInputTokens,
        totalOutputTokens: totalOutputTokens,
        activeRequests: activeRequests
      };
    } else {
      // Use global stats
      stats = requestStore.getStats();
    }
    
    res.json(stats);
  } catch (error) {
    logger.error(`Monitor API error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/monitor/export', (req, res) => {
  try {
    // Get filtered data based on query parameters
    const filters = {
      status: req.query.status,
      model: req.query.model,
      timeRange: req.query.timeRange
    };
    
    // Remove empty filters
    Object.keys(filters).forEach(key => {
      if (!filters[key]) delete filters[key];
    });
    
    const filteredRequests = requestStore.getAll(filters);
    
    // Export filtered data
    const exportData = {
      stats: requestStore.getStats(),
      requests: filteredRequests.data,
      total: filteredRequests.total,
      filters: filters,
      exportTime: new Date().toISOString()
    };
    
    const filterSuffix = Object.keys(filters).length > 0 ? '-filtered' : '';
    res.setHeader('Content-Type', 'application/json');
    res.setHeader('Content-Disposition', `attachment; filename="proxy-monitor${filterSuffix}-${Date.now()}.json"`);
    res.json(exportData);
  } catch (error) {
    logger.error(`Monitor API error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.post('/api/monitor/clear', (req, res) => {
  try {
    requestStore.clear();
    res.json({ success: true });
  } catch (error) {
    logger.error(`Monitor API error: ${error.message}`);
    res.status(500).json({ error: error.message });
  }
});

app.get('/api/monitor/analyze', (req, res) => {
  try {
    logger.info('Generating analysis report...');
    
    // Get filters from query parameters
    const filters = {
      status: req.query.status,
      model: req.query.model,
      timeRange: req.query.timeRange
    };
    
    // Remove empty filters
    Object.keys(filters).forEach(key => {
      if (!filters[key]) delete filters[key];
    });
    
    const analysisData = analyzeRequests(filters);
    const htmlReport = generateAnalysisHTML(analysisData, filters);
    
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.send(htmlReport);
    
    const filterInfo = Object.keys(filters).length > 0 ? ` with filters: ${JSON.stringify(filters)}` : '';
    logger.info(`Analysis report generated with ${analysisData.requests.length} requests${filterInfo}`);
  } catch (error) {
    logger.error(`Analysis generation error: ${error.message}`, error.stack);
    res.status(500).json({ 
      error: 'Failed to generate analysis report',
      message: error.message 
    });
  }
});

// Server-Sent Events for real-time updates
app.get('/api/monitor/stream', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Headers': '*'
  });
  
  // Send initial stats
  const stats = requestStore.getStats();
  res.write(`data: ${JSON.stringify({ type: 'stats', stats })}\n\n`);
  
  // Set up event listeners
  const onRequestStart = (request) => {
    res.write(`data: ${JSON.stringify({ type: 'request', request })}\n\n`);
  };
  
  const onRequestEnd = (request) => {
    res.write(`data: ${JSON.stringify({ type: 'request', request })}\n\n`);
    res.write(`data: ${JSON.stringify({ type: 'stats', stats: requestStore.getStats() })}\n\n`);
  };
  
  const onRequestError = (request) => {
    res.write(`data: ${JSON.stringify({ type: 'request', request })}\n\n`);
    res.write(`data: ${JSON.stringify({ type: 'stats', stats: requestStore.getStats() })}\n\n`);
  };
  
  const onStreamChunk = ({ id, chunk }) => {
    const request = requestStore.getById(id);
    if (request) {
      res.write(`data: ${JSON.stringify({ type: 'request', request })}\n\n`);
    }
  };
  
  // Register listeners
  requestStore.on('requestStart', onRequestStart);
  requestStore.on('requestEnd', onRequestEnd);
  requestStore.on('requestError', onRequestError);
  requestStore.on('streamChunk', onStreamChunk);
  
  // Clean up on client disconnect
  req.on('close', () => {
    requestStore.off('requestStart', onRequestStart);
    requestStore.off('requestEnd', onRequestEnd);
    requestStore.off('requestError', onRequestError);
    requestStore.off('streamChunk', onStreamChunk);
  });
  
  // Keep connection alive
  const keepAlive = setInterval(() => {
    res.write(': keep-alive\n\n');
  }, 30000);
  
  req.on('close', () => {
    clearInterval(keepAlive);
  });
});

/**
 * Start the server
 */
function main() {
  console.log('='.repeat(60));
  console.log('🚀 Anthropic API Proxy Server');
  console.log('='.repeat(60));
  console.log('✅ Configuration:');
  console.log(`   Target API: ${ANTHROPIC_BASE_URL}`);
  console.log(`   Timeout: ${REQUEST_TIMEOUT}ms`);
  console.log(`   Server: http://${HOST}:${PORT}`);
  console.log(`   Monitor: http://${HOST}:${PORT}/monitor`);
  console.log('='.repeat(60));
  console.log('ℹ️  Note: Client must provide API key via headers');
  console.log('='.repeat(60));
  console.log();
  
  const server = createServer(app);
  
  server.listen(PORT, HOST, () => {
    logger.info('='.repeat(60));
    logger.info('🚀 Anthropic API Proxy Server Started');
    logger.info(`🌐 Listening on: http://${HOST}:${PORT}`);
    logger.info(`🎯 Target API: ${ANTHROPIC_BASE_URL}`);
    logger.info(`⚙️  Log Level: ${LOG_LEVEL}`);
    logger.info(`⏱️  Request Timeout: ${REQUEST_TIMEOUT}ms`);
    
    // Log authentication method
    if (USE_OAUTH && oauthManager.isAvailable()) {
      logger.info('🔐 Authentication: OAuth (with auto-refresh)');
      const expiryInfo = oauthManager.getExpiryInfo();
      if (expiryInfo) {
        logger.info(`   Token expires: ${expiryInfo.expiresAt}`);
        logger.info(`   Auto-refresh: ${expiryInfo.expiresInMinutes > 10 ? expiryInfo.expiresInMinutes - 10 : 0} minutes before expiry`);
      }
    } else if (ANTHROPIC_API_KEY) {
      logger.info('🔑 Authentication: API Key');
    } else {
      logger.info('⚠️  Authentication: Pass-through mode');
    }
    
    // Log user authentication status
    if (userAuthManager.isEnabled()) {
      const status = userAuthManager.getStatus();
      logger.info('👤 User Authentication: Enabled');
      logger.info(`   Users: ${status.userCount} users configured`);
    } else {
      logger.info('👤 User Authentication: Disabled (no .userlist file)');
    }
    
    logger.info('='.repeat(60));
    logger.info('🔗 Endpoints:');
    logger.info('  - POST /v1/messages');
    logger.info('  - POST /v1/messages/count_tokens');
    logger.info('  - GET  /health');
    logger.info('  - GET  /monitor (Web UI)');
    logger.info('  - GET  /status (Proxy status)');
    logger.info('  - GET  /api/oauth/status (OAuth status)');
    logger.info('  - GET  /api/oauth/refresh (Force token refresh)');
    logger.info('  - POST /api/oauth/refresh/all (Refresh all tokens)');
    logger.info('  - POST /api/oauth/switch/:index (Switch token)');
    logger.info('  - POST /api/oauth/reload (Reload tokens from file)');
    logger.info('='.repeat(60));
    logger.info('📊 Monitor Dashboard: http://' + (HOST === '0.0.0.0' ? 'localhost' : HOST) + ':' + PORT + '/monitor');
    logger.info('👥 User Management: http://' + (HOST === '0.0.0.0' ? 'localhost' : HOST) + ':' + PORT + '/users');
    logger.info('='.repeat(60));
  });
  
  // Handle graceful shutdown
  let isShuttingDown = false;
  
  const gracefulShutdown = (signal) => {
    if (isShuttingDown) {
      logger.warn('Force shutdown - exiting immediately');
      process.exit(1);
    }
    
    isShuttingDown = true;
    logger.info(`${signal} signal received: starting graceful shutdown`);
    
    // Clean up OAuth timers
    if (oauthManager) {
      oauthManager.cleanup();
    }
    
    // Set a timeout to force exit if graceful shutdown takes too long
    const forceExitTimeout = setTimeout(() => {
      logger.error('Graceful shutdown timeout - forcing exit');
      process.exit(1);
    }, 10000); // 10 seconds timeout
    
    server.close((err) => {
      clearTimeout(forceExitTimeout);
      if (err) {
        logger.error('Error during server shutdown:', err);
        process.exit(1);
      }
      logger.info('HTTP server closed successfully');
      process.exit(0);
    });
    
    // Also close any active connections
    server.closeAllConnections?.();
  };
  
  process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
  process.on('SIGINT', () => gracefulShutdown('SIGINT'));
  
  // Handle uncaught exceptions
  process.on('uncaughtException', (err) => {
    logger.error('Uncaught Exception:', err);
    gracefulShutdown('UNCAUGHT_EXCEPTION');
  });
  
  process.on('unhandledRejection', (reason, promise) => {
    logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
    gracefulShutdown('UNHANDLED_REJECTION');
  });
}

// Run the server
main();